/* JAI-Ext - OpenSource Java Advanced Image Extensions Library
*    http://www.geo-solutions.it/
*    Copyright 2014 GeoSolutions


* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at

* http://www.apache.org/licenses/LICENSE-2.0

* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.eclipse.imagen.media.nullop;

import java.awt.Rectangle;
import java.awt.image.ColorModel;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.util.Hashtable;
import java.util.Map;
import org.eclipse.imagen.ImageLayout;
import org.eclipse.imagen.OpImage;
import org.eclipse.imagen.PlanarImage;
import org.eclipse.imagen.PointOpImage;
import org.eclipse.imagen.media.util.JDKWorkarounds;

/**
 * An<code>OpImage</code> subclass that simply transmits its source unchanged. This may be useful when an interface
 * requires an <code>OpImage</code> but another sort of <code>RenderedImage</code> (such as a <code>BufferedImage</code>
 * or <code>TiledImage</code>) is available. Additionally, <code>NullOpImage</code> is able to make use of JAI's tile
 * caching mechanisms.
 *
 * <p>Methods that get or set properties are implemented to forward the requests to the source image; no independent
 * property information is stored in the <code>NullOpImage</code> itself.
 *
 * @see PointOpImage
 */
public class NullOpImage extends OpImage {

    /** Create a new ImageLayout from the source image optionally overriding a ColorModel supplied via the layout. */
    private static ImageLayout layoutHelper(RenderedImage source, ImageLayout layout) {
        // Create basic layout from the source.
        ImageLayout il = new ImageLayout(source);

        // If a layout containing a valid ColorModel field is supplied then
        // reset the ColorModel if it is compatible with the SampleModel.
        if (layout != null && layout.isValid(ImageLayout.COLOR_MODEL_MASK)) {
            ColorModel colorModel = layout.getColorModel(null);
            if (JDKWorkarounds.areCompatibleDataModels(source.getSampleModel(), colorModel)) {
                il.setColorModel(colorModel);
            }
        }

        return il;
    }

    /**
     * Constructs a <code>NullOpImage</code>. The superclass constructor will be passed a new <code>ImageLayout</code>
     * object with all of its fields filled in.
     *
     * @param layout An <code>ImageLayout</code> optionally specifying the image <code>ColorModel</code>; all other
     *     fields are ignored. This parameter may be <code>null</code>.
     * @param source A <code>RenderedImage</code>; must not be <code>null</code> or a <code>IllegalArgumentException
     *     </code> will be thrown.
     * @param configuration Configurable attributes of the image including configuration variables indexed by <code>
     *     RenderingHints.Key</code>s and image properties indexed by <code>String</code>s or <code>CaselessStringKey
     *     </code>s. This is simply forwarded to the superclass constructor.
     * @throws <code>IllegalArgumentException</code> if <code>source</code> is <code>null</code>.
     * @throws <code>IllegalArgumentException</code> if <code>computeType</code> is not one of the known <code>
     *     OP_*_BOUND</code> values.
     */
    public NullOpImage(RenderedImage source, ImageLayout layout, Map configuration) {
        super(vectorize(source), layoutHelper(source, layout), configuration, true);
    }

    /**
     * Returns a tile for reading.
     *
     * @param tileX The X index of the tile.
     * @param tileY The Y index of the tile.
     * @return The tile as a <code>Raster</code>.
     */
    public Raster computeTile(int tileX, int tileY) {
        return getSourceImage(0).getTile(tileX, tileY);
    }

    /** Returns false as NullOpImage can return via computeTile() tiles that are internally cached. */
    public boolean computesUniqueTiles() {
        return false;
    }

    /** Returns the properties from the source image. */
    protected synchronized Hashtable getProperties() {
        // Selection of all the image properties
        String[] propertyNames = getPropertyNames();
        // Selection of the source image
        RenderedImage sourceImage = getSourceImage(0);
        // Creation of a Map containing all the source image properties
        Hashtable<String, Object> properties = new Hashtable<String, Object>();
        // Cycle on all the properties
        if (propertyNames != null) {
            for (String property : propertyNames) {
                // Addition of the selected property
                properties.put(property, sourceImage.getProperty(property));
            }
            return properties;
        } else {
            return null;
        }
    }

    /** Set the properties <code>Hashtable</code> of the source image to the supplied <code>Hashtable</code>. */
    protected synchronized void setProperties(Hashtable properties) {
        // Selection of the source image
        PlanarImage sourceImage = getSourceImage(0);

        Hashtable<String, Object> propertyTable = properties;
        if (propertyTable != null) {
            // Cycle on all the properties
            for (String property : propertyTable.keySet()) {
                // Setting of the selected property
                sourceImage.setProperty(property, propertyTable.get(property));
            }
        }
    }

    /** Returns the property names from the source image or <code>null</code> if no property names are recognized. */
    public String[] getPropertyNames() {
        return getSourceImage(0).getPropertyNames();
    }

    /**
     * Returns the property names with the supplied prefix from the source image or <code>null</code> if no property
     * names are recognized.
     */
    public String[] getPropertyNames(String prefix) {
        return getSourceImage(0).getPropertyNames(prefix);
    }

    /** Returns the class of the specified property from the source image. */
    public Class getPropertyClass(String name) {
        return getSourceImage(0).getPropertyClass(name);
    }

    /**
     * Retrieves a property from the source image by name or <code>java.awt.Image.UndefinedProperty</code> if the
     * property with the specified name is not defined.
     */
    public Object getProperty(String name) {
        return getSourceImage(0).getProperty(name);
    }

    /** Sets a property on the source image by name. */
    public void setProperty(String name, Object value) {
        getSourceImage(0).setProperty(name, value);
    }

    /** Removes a property from the source image by name. */
    public void removeProperty(String name) {
        getSourceImage(0).removeProperty(name);
    }

    @Override
    public Rectangle mapDestRect(Rectangle destRect, int sourceIndex) {
        return destRect;
    }

    @Override
    public Rectangle mapSourceRect(Rectangle sourceRect, int sourceIndex) {
        return sourceRect;
    }
}
